package com.common.shiro;

import java.util.Calendar;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.springframework.beans.factory.annotation.Autowired;

import com.common.dao.AccountInfoDAO;
import com.common.dao.LogUserFailDAO;
import com.common.dto.AccountInfoDTO;
import com.common.dto.LogUserFailDTO;
import com.common.redis.RedisClient;
import com.common.redis.RedisUtil;
import com.common.service.AccountInfoService;

/**
 * 登录密码偿试次数
 */
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {
	//也可以用ehcache
	private Cache<String, AtomicInteger> passwordRetryCache;

	@Autowired
	private AccountInfoService accountInfoService;

	@Autowired
	private LogUserFailDAO logUserFailDAO;
	
	@Autowired
	private AccountInfoDAO accountInfoDAO;
	
	@Autowired
	private RedisUtil redisUtil;

	public RetryLimitHashedCredentialsMatcher() {

	}

	public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager) {
		passwordRetryCache = cacheManager.getCache("passwordRetryCache");
	}
	
	@Override
	public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
		boolean matches = super.doCredentialsMatch(token, info);
		String username = (String) token.getPrincipal();
		AccountInfoDTO accountPara = new AccountInfoDTO();
		accountPara.setAccountCode(username);
		accountPara.setPersonCode(username);
		AccountInfoDTO account = accountInfoService.getAccountInfo(accountPara);
		String accountStatus = account.getStatus();
		if ("2".equals(accountStatus)) {// 说明账号目前还处于锁定状态
			redisUtil.del(username);
			throw new ExcessiveAttemptsException("账号已经锁定,请联系管理员");
			//unlockAccount(matches, username, account);2小时到了可以解锁用户账号，根据需要调用 
		} else {
			if (!matches) {//
				//AtomicInteger retryCount = passwordRetryCache.get(username);
				AtomicInteger retryCount = (AtomicInteger) redisUtil.get(username);
				if (retryCount == null) {
					retryCount = new AtomicInteger(0);
					//passwordRetryCache.put(username, retryCount);
					redisUtil.set(username, retryCount);
				} else {
					if (retryCount.get() > 4) {
						// if retry count > 5 throw
						String personCode = account.getPersonCode();
						LogUserFailDTO logUserFailDTO = new LogUserFailDTO();
						logUserFailDTO.setCreator(personCode);
						logUserFailDTO.setCreateTime(Calendar.getInstance().getTime());
						logUserFailDTO.setModifier(personCode);
						logUserFailDTO.setModifyTime(Calendar.getInstance().getTime());
						logUserFailDTO.setStatus("1");
						logUserFailDTO.setFailCount(retryCount.decrementAndGet());
						logUserFailDAO.insert(logUserFailDTO);//插入一条错误尝试记录logUserFailDTO到数据库中
						account.setStatus("2");
						account.setModifier(personCode);
						account.setModifyTime(Calendar.getInstance().getTime());
						accountInfoDAO.updateByPrimaryKey(account);//将当前尝试登录的账号设置为锁定
						//账号已锁定，请两小时后再尝试登录
						throw new ExcessiveAttemptsException("账号已锁定，请联系管理员");
					} else {
						retryCount.incrementAndGet();
						redisUtil.set(username, retryCount);
						throw new ExcessiveAttemptsException("账号密码错误,您还可以再尝试 "+ (5 -retryCount.get())+"次");
					}
				} 
			}else {
				//AtomicInteger retryCount = passwordRetryCache.get(username);
				AtomicInteger retryCount = (AtomicInteger) redisUtil.get(username);
				if (retryCount != null) {
					//passwordRetryCache.remove(username);
					redisUtil.del(username);
				}
			}
		}
		// retry count + 1
		return matches;

	}
	
	/**
	 * 2小时到了可以解锁用户账号，根据需要调用 
	 * @param matches
	 * @param username
	 * @param account
	 */
	private void unlockAccount(boolean matches, String username, AccountInfoDTO account) {
		LogUserFailDTO logUserFailDTO = new LogUserFailDTO();
		logUserFailDTO.setCreator(username);
		logUserFailDTO.setStatus("1");
		logUserFailDTO = logUserFailDAO.selectOne(logUserFailDTO);// 获取锁定账户登录尝试的次数
		int failCount = 0;
		int timeInterval;
		if (null != logUserFailDTO) {
			failCount = logUserFailDTO.getFailCount();
			int hours = (int) Math.pow(2, failCount - 3);// 计算锁定时间
			// 计算上次最后尝试登陆的时间与现在相隔多少分钟
			int lastTryTime =(int) ((System.currentTimeMillis() - account.getModifyTime().getTime())/1000/60);//DateUtil.minutesBetween(account.getModifyTime(), DateUtil.getSystemTimeStr());
			timeInterval = hours * 60 - lastTryTime;
		} else {
			timeInterval = -1;
		}
		if (timeInterval >= 0) {// 还没过锁定时间，禁止登陆
			if (timeInterval > 60) {
				int hour = timeInterval / 60;
				throw new ExcessiveAttemptsException("账号已经锁定,请" + (hour + 1) + "小时后登录");
			} else {
				throw new ExcessiveAttemptsException("账号已经锁定,请" + timeInterval + "分钟后登录");
			}
		} else {//锁定时间已过，进行登录验证
			if (matches) {//登录验证成功,将当前尝试登录的账号设置为解锁，并且删除锁定信息
				// clear retry count
				account.setStatus("1");
				account.setModifier(username);
				account.setModifyTime(Calendar.getInstance().getTime());
				accountInfoDAO.updateByPrimaryKey(account);//设置账号状态
				logUserFailDTO.setStatus("0");
				logUserFailDTO.setModifier(username);
				logUserFailDTO.setModifyTime(Calendar.getInstance().getTime());;
				logUserFailDAO.updateByPrimaryKey(logUserFailDTO);//删除锁定信息
				passwordRetryCache.remove(username);//清除错误舱室缓存
			}else {//验证失败，继续锁定账户，更新account修改时间，并且更新锁定信息
				logUserFailDTO.setFailCount(failCount+1);
				logUserFailDAO.updateByPrimaryKey(logUserFailDTO);
				account.setModifier(username);
				account.setModifyTime(Calendar.getInstance().getTime());
				accountInfoDAO.updateByPrimaryKey(account);
				throw new ExcessiveAttemptsException("账号已锁定，请联系管理员");
			}
		}
	}
}
